Skip to content

[None][feat] Enable PyTorch profiler traces for VisualGen#16814

Draft
chang-l wants to merge 4 commits into
NVIDIA:mainfrom
chang-l:agent/visual-gen-torch-profiler
Draft

[None][feat] Enable PyTorch profiler traces for VisualGen#16814
chang-l wants to merge 4 commits into
NVIDIA:mainfrom
chang-l:agent/visual-gen-torch-profiler

Conversation

@chang-l

@chang-l chang-l commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Dev Engineer Review

  • Added optional VisualGen PyTorch profiler trace support gated by TLLM_TORCH_PROFILE_TRACE, recording CPU/CUDA/XPU activity with shapes/module metadata and exporting rank-suffixed Chrome trace files (visual-gen-trace-rank-*.json, plus -window-N when multiple capture windows are used).
  • Wired profiler lifecycle to the existing VisualGen CUDA/Nsight profiling ranges via TLLM_PROFILE_VISUAL_GEN_START_STOP, including phase handling for predenoise, postdenoise, and all, with “warmup” aware behavior.
  • Refactored VisualGen profiling window logic in the pipeline to coordinate CUDA profiler start/stop with the torch.profiler.profile session and ensured trace export occurs only for active profiling windows.
  • Instrumented Qwen image denoising to add step-level profiling hooks (_profile_denoise_start/_end, _start_step_profile/_stop_step_profile) around the denoise loop.
  • Updated executor and unit-test scaffolding/documentation to use pipeline.run_inference() (replacing infer() in relevant call sites and failure-propagation tests).
  • Docs: updated the performance-analysis guide with VisualGen profiling env var behavior, phase/range mapping to denoise steps, and example usage.

QA Engineer Review

Test functions added/updated

  • tests/unittest/_torch/visual_gen/test_profiler.py
    • Added/expanded:
      • test_setup_torch_profiler
      • test_setup_torch_profiler_requires_profile_range
      • test_cuda_profiler_controls_torch_profiler
      • test_cuda_profiler_uses_fresh_trace_for_each_window
      • test_cuda_profiler_closes_cuda_gate_when_trace_export_fails
      • test_run_inference_owns_request_profile_boundaries (parameterized)
      • test_run_inference_defers_predenoise_profile_until_after_warmup
  • tests/unittest/_torch/visual_gen/test_qwen_image_pipeline.py
    • Added: test_forward_honors_profile_step_range
  • Other unit test updates (no new function list provided in summary):
    • tests/unittest/_torch/visual_gen/test_visual_gen_params.py
    • tests/unittest/visual_gen/test_executor_shared_tensor_ipc.py

Coverage in test lists

  • Modified tests/integration/test_lists/test-db/l0_a10.yml: added unittest/_torch/visual_gen/test_profiler.py under the visual_gen section.
  • No corresponding test-list entries were indicated for tests/unittest/_torch/visual_gen/test_qwen_image_pipeline.py or the other updated VisualGen unit tests.

Verdict: insufficienttest_profiler.py is added to test-db, but the additional/updated VisualGen profiler-related tests don’t appear to be covered by the updated test-list entries.

++ BrianLi23 for viz

Description

VisualGen already uses TLLM_PROFILE_VISUAL_GEN_START_STOP to gate Nsight Systems capture, but it could not emit PyTorch/Kineto traces like the LLM executor.

This change starts an optional torch.profiler.profile session on the same VisualGen ranges when TLLM_TORCH_PROFILE_TRACE is set:

  • all now covers the complete user request from text encoding through denoising and VAE decode.
  • predenoise covers request start through the denoise-loop boundary; postdenoise covers that boundary through request completion.
  • Numeric ranges continue to select denoise steps per request.
  • A shared BasePipeline.run_inference() wrapper owns request-level start/stop and exception cleanup. Base denoise and Qwen-Image's custom true-CFG loop call the same semantic phase/step hooks, so Qwen does not duplicate profiler setup or teardown.
  • Warmups are excluded. Each rank and repeated capture window writes a distinct Chrome trace file.
  • The trace records CPU plus the available accelerator activity and tensor shapes. There are no public API or dependency changes.

cudaProfilerStart()/cudaProfilerStop() remain capture gates for an attached Nsight collector; they do not start Nsight on their own. Torch and Nsight collectors should be run in separate invocations because both use CUPTI.

Test Coverage

  • Added profiler lifecycle coverage for setup, missing-range behavior, all/pre/post request boundaries, warmup deferral, repeated non-overwriting windows, and export-failure cleanup.
  • Added Qwen-Image numeric-range coverage and registered test_profiler.py in l0_a10.yml.
  • Ran pre-commit on all nine changed files; all hooks passed.
  • On a shared B200 node, focused pytest coverage passed: 13 passed, 38 warnings in 143.25s.
  • On the same B200, Wan 2.1 T2V 1.3B completed a 480x832, 17-frame request with exactly 5 denoise steps and error=None using TLLM_PROFILE_VISUAL_GEN_START_STOP=all.
  • The exported 47,756,268-byte trace is valid JSON with 128,681 events, including text-encoder embedding ops, 15,250 GPU kernels across the full request, and VAE-decode convolution ops after denoising.
  • A separate five-step Nsight Systems run also completed with exit code 0 and observed the same application capture start/stop gates.

PR Checklist

  • PR description clearly explains what and why.

  • PR follows the TRT-LLM coding guidelines.

  • Test cases are provided for the new code paths and are routed in the test database.

  • No public API changes or new dependencies.

  • Documentation is updated.

  • No CODEOWNERS or architecture-diagram change is required.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, comment /bot help.

Signed-off-by: Chang Liu <9713593+chang-l@users.noreply.github.com>
@chang-l
chang-l marked this pull request as ready for review July 23, 2026 23:31
@chang-l
chang-l requested review from a team as code owners July 23, 2026 23:31
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

VisualGen profiling now supports environment-controlled PyTorch traces coordinated with CUDA profiler ranges, denoising-phase hooks, rank-specific trace output, executor integration through run_inference, expanded tests, and updated profiling guidance.

Changes

VisualGen profiling and inference integration

Layer / File(s) Summary
Profiling configuration and initialization
tensorrt_llm/_torch/visual_gen/pipeline.py
Adds profiling environment constants, updates range parsing, initializes optional PyTorch profiling, and generates rank-aware trace paths.
Profiler lifecycle and denoising integration
tensorrt_llm/_torch/visual_gen/pipeline.py, tensorrt_llm/_torch/visual_gen/models/qwen_image/pipeline_qwen_image.py
Coordinates CUDA and PyTorch profiler windows across request, denoise, and per-step boundaries.
Inference entrypoint and profiling boundaries
tensorrt_llm/_torch/visual_gen/executor.py, tests/unittest/visual_gen/test_executor_shared_tensor_ipc.py, tests/unittest/_torch/visual_gen/test_profiler.py, tests/unittest/_torch/visual_gen/test_visual_gen_params.py
Routes executor requests through run_inference and validates profiling event ordering and failure propagation.
Profiling validation and usage guidance
tests/unittest/_torch/visual_gen/test_profiler.py, tests/unittest/_torch/visual_gen/test_qwen_image_pipeline.py, tests/integration/test_lists/test-db/l0_a10.yml, docs/source/developer-guide/perf-analysis.md
Tests profiler setup, trace windows, cleanup, and Qwen image hooks; adds A10 test-list wiring and VisualGen profiling instructions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DiffusionExecutor
  participant BasePipeline
  participant QwenImagePipeline
  participant CUDAProfiler
  participant TorchProfiler
  DiffusionExecutor->>BasePipeline: Call run_inference(req)
  BasePipeline->>CUDAProfiler: Start configured profiling window
  BasePipeline->>TorchProfiler: Start tracing
  BasePipeline->>QwenImagePipeline: Execute generation phases and denoise steps
  QwenImagePipeline->>BasePipeline: Signal denoise and step boundaries
  BasePipeline->>CUDAProfiler: Stop profiling window
  BasePipeline->>TorchProfiler: Stop and export rank-specific trace
  BasePipeline-->>DiffusionExecutor: Return PipelineOutput
Loading

Suggested reviewers: zhenhuaw-me, xinhe-nv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise, specific, and accurately describes the main change: enabling PyTorch profiler traces for VisualGen.
Description check ✅ Passed The description follows the template with Description, Test Coverage, and PR Checklist sections and includes relevant details.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tensorrt_llm/_torch/visual_gen/pipeline.py (1)

191-212: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Annotate profiler lifecycle methods.

Add -> None to both methods.

-    def _cuda_profiler_start(self):
+    def _cuda_profiler_start(self) -> None:
...
-    def _cuda_profiler_stop(self):
+    def _cuda_profiler_stop(self) -> None:

As per coding guidelines, “Annotate every function, use None for non-returning functions.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/visual_gen/pipeline.py` around lines 191 - 212, Add the
`-> None` return annotation to both `_cuda_profiler_start` and
`_cuda_profiler_stop`, preserving their existing profiler lifecycle behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_torch/visual_gen/pipeline.py`:
- Around line 178-179: Update the trace path construction around
self._torch_profile_trace_path so each profiling window receives a unique output
filename instead of reusing the same rank-based path. Add and maintain a window
index across repeated A-B,C-D profiling stops, incorporating it into the
exported trace path while preserving the existing rank and extension components.

In `@tests/unittest/_torch/visual_gen/test_profiler.py`:
- Around line 17-88: The profiler tests cover only a single start/stop window;
add a test for multiple configured ranges such as 0-1 and 3-4, asserting the
corresponding profiling behavior across both windows. Also register
tests/unittest/_torch/visual_gen/test_profiler.py in the relevant test-db and QA
test-list files, preserving their existing list format.

---

Nitpick comments:
In `@tensorrt_llm/_torch/visual_gen/pipeline.py`:
- Around line 191-212: Add the `-> None` return annotation to both
`_cuda_profiler_start` and `_cuda_profiler_stop`, preserving their existing
profiler lifecycle behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ce070dc7-3ebc-4306-babd-3cbb37cc95e9

📥 Commits

Reviewing files that changed from the base of the PR and between 80b4eb3 and d787ad9.

📒 Files selected for processing (3)
  • docs/source/developer-guide/perf-analysis.md
  • tensorrt_llm/_torch/visual_gen/pipeline.py
  • tests/unittest/_torch/visual_gen/test_profiler.py

Comment thread tensorrt_llm/_torch/visual_gen/pipeline.py
Comment thread tests/unittest/_torch/visual_gen/test_profiler.py
chang-l added 2 commits July 23, 2026 21:37
Signed-off-by: Chang Liu <9713593+chang-l@users.noreply.github.com>
Signed-off-by: Chang Liu <9713593+chang-l@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tensorrt_llm/_torch/visual_gen/pipeline.py (2)

181-190: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Select the profiler activity at runtime. torch.profiler.profile() should not be given both CUDA and XPU unconditionally; on builds that only support one accelerator backend, profiler setup can fail. Build the list from the supported activities or branch on the active device so the trace requests only CPU + the matching accelerator.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/visual_gen/pipeline.py` around lines 181 - 190, Update
the profiler setup around self._torch_profiler and activities to select only the
accelerator activity supported by the active device or build, always retaining
CPU while excluding the incompatible CUDA/XPU activity. Pass the resulting
CPU-plus-matching-accelerator list to torch.profiler.profile.

189-189: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

with_modules=True won’t record module hierarchy here — VisualGen uses eager nn.Modules, so the profiler trace won’t include module info unless this path is scripted/traced or you add explicit instrumentation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/visual_gen/pipeline.py` at line 189, The profiler
configuration using with_modules=True does not capture hierarchy for VisualGen’s
eager nn.Module execution. Remove this ineffective option or replace it with
explicit module instrumentation, ensuring module hierarchy is recorded only
through a supported mechanism.
🧹 Nitpick comments (1)
tests/unittest/_torch/visual_gen/test_qwen_image_pipeline.py (1)

226-253: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Expand profiler lifecycle coverage

test_forward_honors_profile_step_range only covers the 0-1 path. Add focused cases for predenoise, all, postdenoise, warmup gating, and the final inactive state; the postdenoise case should assert that tracing stops and exports.

Coverage summary: added test_forward_honors_profile_step_range; listed in tests/integration/test_lists/test-db/l0_a10.yml; no matching entry in tests/integration/test_lists/qa/. Coverage verdict: needs follow-up.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/visual_gen/test_qwen_image_pipeline.py` around lines
226 - 253, Expand test_forward_honors_profile_step_range with focused profiler
lifecycle cases covering predenoise, all, postdenoise, warmup gating, and the
final inactive state. Configure each profile range and assert the corresponding
CUDA and torch profiler calls, especially that postdenoise stops tracing and
exports the configured trace. Keep the tests isolated with the existing pipeline
test doubles and mocks.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_torch/visual_gen/models/qwen_image/pipeline_qwen_image.py`:
- Around line 528-530: Update the forward path around
_start_postdenoise_profile() so post-denoise profiling is stopped and exported
after _decode_latents() completes. Use a finally block to call the corresponding
stop operation even when decoding raises, ensuring profiler state is not left
active and the Chrome trace is exported.
- Around line 488-494: Move the `_start_predenoise_profile()` call earlier in
the request flow, before timestep creation and scheduler preparation, so
pre-loop scheduler refresh work is included in profiling. Keep
`_start_denoise_profile()` immediately before the denoising loop.

---

Outside diff comments:
In `@tensorrt_llm/_torch/visual_gen/pipeline.py`:
- Around line 181-190: Update the profiler setup around self._torch_profiler and
activities to select only the accelerator activity supported by the active
device or build, always retaining CPU while excluding the incompatible CUDA/XPU
activity. Pass the resulting CPU-plus-matching-accelerator list to
torch.profiler.profile.
- Line 189: The profiler configuration using with_modules=True does not capture
hierarchy for VisualGen’s eager nn.Module execution. Remove this ineffective
option or replace it with explicit module instrumentation, ensuring module
hierarchy is recorded only through a supported mechanism.

---

Nitpick comments:
In `@tests/unittest/_torch/visual_gen/test_qwen_image_pipeline.py`:
- Around line 226-253: Expand test_forward_honors_profile_step_range with
focused profiler lifecycle cases covering predenoise, all, postdenoise, warmup
gating, and the final inactive state. Configure each profile range and assert
the corresponding CUDA and torch profiler calls, especially that postdenoise
stops tracing and exports the configured trace. Keep the tests isolated with the
existing pipeline test doubles and mocks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: dbb8a4b9-421d-4cb2-bc91-1fad5661b0e9

📥 Commits

Reviewing files that changed from the base of the PR and between d787ad9 and f930d1a.

📒 Files selected for processing (4)
  • docs/source/developer-guide/perf-analysis.md
  • tensorrt_llm/_torch/visual_gen/models/qwen_image/pipeline_qwen_image.py
  • tensorrt_llm/_torch/visual_gen/pipeline.py
  • tests/unittest/_torch/visual_gen/test_qwen_image_pipeline.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/source/developer-guide/perf-analysis.md

Comment thread tensorrt_llm/_torch/visual_gen/models/qwen_image/pipeline_qwen_image.py Outdated
Comment on lines +528 to +530
self._stop_step_profile(i)

self._start_postdenoise_profile()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Stop and export post-denoise profiling before returning.

When post-denoise profiling is pending, Line 530 starts the CUDA and PyTorch profilers, but this forward() path never stops them after _decode_latents(). Consequently, post-denoise mode does not export its Chrome trace and can leave profiler state active. Stop it after decoding, preferably in a finally block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/visual_gen/models/qwen_image/pipeline_qwen_image.py`
around lines 528 - 530, Update the forward path around
_start_postdenoise_profile() so post-denoise profiling is stopped and exported
after _decode_latents() completes. Use a finally block to call the corresponding
stop operation even when decoding raises, ensuring profiler state is not left
active and the Chrome trace is exported.

Signed-off-by: Chang Liu <9713593+chang-l@users.noreply.github.com>
@chang-l
chang-l requested review from a team as code owners July 24, 2026 06:35
@chang-l
chang-l marked this pull request as draft July 24, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant